Implement doc tests
authorAlex Crichton <alex@alexcrichton.com>
Fri, 1 Aug 2014 03:21:13 +0000 (20:21 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 5 Aug 2014 17:46:12 +0000 (10:46 -0700)
Whenever `cargo test` is run and a testable library target is available, the doc
tests will be run. This can be opted out of with `test = false` as usual.

This is currently not super useful due to rust-lang/rust#16157, but I expect
that to be merged soon. In the meantime examples will need to `extern crate foo`
explicitly.

src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/ops/cargo_test.rs
src/cargo/util/toml.rs
tests/support/mod.rs
tests/test_cargo_freshness.rs
tests/test_cargo_test.rs

index dabbd8e969bc0d1e97eaf184ee8950ce55b8200f..3b605e1a99e1edbb000984632df6494ad112bbc2 100644 (file)
@@ -44,7 +44,8 @@ pub struct CompileOptions<'a> {
 }
 
 pub fn compile(manifest_path: &Path,
-               options: &mut CompileOptions) -> CargoResult<()> {
+               options: &mut CompileOptions)
+               -> CargoResult<HashMap<PackageId, Vec<Path>>> {
     let CompileOptions { update, env, ref mut shell, jobs, target } = *options;
     let target = target.map(|s| s.to_string());
 
@@ -114,7 +115,7 @@ pub fn compile(manifest_path: &Path,
         }
     }).collect::<Vec<&Target>>();
 
-    {
+    let ret = {
         let _p = profile::start("compiling");
         let mut config = try!(Config::new(*shell, update, jobs, target));
         try!(scrape_target_config(&mut config, &user_configs));
@@ -122,12 +123,12 @@ pub fn compile(manifest_path: &Path,
         try!(ops::compile_targets(env.as_slice(), targets.as_slice(), &package,
                                   &PackageSet::new(packages.as_slice()),
                                   &resolve_with_overrides, &sources,
-                                  &mut config));
-    }
+                                  &mut config))
+    };
 
     try!(ops::write_resolve(&package, &resolve));
 
-    Ok(())
+    return Ok(ret);
 }
 
 fn source_ids_from_config(configs: &HashMap<String, config::ConfigValue>,
index 8e8e80482ad6b3fee9d14962b253c075794ad9c1..f11b9dd7ddc1cdc1dd3fa518ffc3a0bd6b4b3740 100644 (file)
@@ -1,4 +1,4 @@
-use std::collections::HashSet;
+use std::collections::{HashSet, HashMap};
 use std::dynamic_lib::DynamicLibrary;
 use std::io::{fs, UserRWX};
 use std::os;
@@ -41,11 +41,15 @@ fn uniq_target_dest<'a>(targets: &[&'a Target]) -> Option<&'a str> {
     curr.unwrap()
 }
 
+// Returns a mapping of the root package plus its immediate dependencies to
+// where the compiled libraries are all located.
 pub fn compile_targets<'a>(env: &str, targets: &[&'a Target], pkg: &'a Package,
-                           deps: &PackageSet, resolve: &'a Resolve, sources: &'a SourceMap,
-                           config: &'a mut Config<'a>) -> CargoResult<()> {
+                           deps: &PackageSet, resolve: &'a Resolve,
+                           sources: &'a SourceMap,
+                           config: &'a mut Config<'a>)
+                           -> CargoResult<HashMap<PackageId, Vec<Path>>> {
     if targets.is_empty() {
-        return Ok(());
+        return Ok(HashMap::new());
     }
 
     debug!("compile_targets; targets={}; pkg={}; deps={}", targets, pkg, deps);
@@ -82,8 +86,12 @@ pub fn compile_targets<'a>(env: &str, targets: &[&'a Target], pkg: &'a Package,
     cx.primary();
     try!(compile(targets, pkg, &mut cx, &mut queue));
 
+    let ret = build_return_map(&cx, pkg, deps);
+
     // Now that we've figured out everything that we're going to do, do it!
-    queue.execute(cx.config)
+    try!(queue.execute(cx.config));
+
+    Ok(ret)
 }
 
 fn compile<'a, 'b>(targets: &[&'a Target], pkg: &'a Package,
@@ -453,3 +461,36 @@ fn pre_version_component(v: &Version) -> Option<String> {
 
     Some(ret)
 }
+
+fn build_return_map(cx: &Context, root: &Package, deps: &PackageSet)
+                    -> HashMap<PackageId, Vec<Path>> {
+    let mut ret = HashMap::new();
+    match cx.resolve.deps(root.get_package_id()) {
+        Some(mut my_deps) => {
+            for dep in my_deps {
+                let pkg = deps.iter().find(|p| p.get_package_id() == dep).unwrap();
+                ret.insert(dep.clone(), build_paths(cx, pkg, false));
+            }
+        }
+        None => {}
+    }
+    ret.insert(root.get_package_id().clone(), build_paths(cx, root, true));
+    return ret;
+
+    fn build_paths(cx: &Context, pkg: &Package, root: bool) -> Vec<Path> {
+        pkg.get_targets().iter().filter(|target| {
+            target.get_profile().is_compile() && target.is_lib()
+        }).flat_map(|target| {
+            let kind = if target.get_profile().is_plugin() {
+                KindPlugin
+            } else {
+                KindTarget
+            };
+            let layout = cx.layout(kind);
+            cx.target_filenames(target).move_iter().map(|filename| {
+                let root = if root {layout.root()} else {layout.deps()};
+                root.join(filename)
+            }).collect::<Vec<Path>>().move_iter()
+        }).collect()
+    }
+}
index 6828235dd3a61cc0513a65f25d342993b6f6596a..60257142e3cccc9cfef437d6d3d475f4ec7d542c 100644 (file)
@@ -1,3 +1,5 @@
+use std::os;
+
 use core::Source;
 use sources::PathSource;
 use ops;
@@ -10,9 +12,9 @@ pub fn run_tests(manifest_path: &Path,
     try!(source.update());
     let package = try!(source.get_root_package());
 
-    try!(ops::compile(manifest_path, options));
+    let compiled_libs = try!(ops::compile(manifest_path, options));
 
-    let mut exes = package.get_targets().iter().filter_map(|target| {
+    let mut exes: Vec<Path> = package.get_targets().iter().filter_map(|target| {
         if !target.get_profile().is_test() { return None }
         let root = package.get_root().join("target");
         let root = match target.get_profile().get_dest() {
@@ -20,14 +22,56 @@ pub fn run_tests(manifest_path: &Path,
             None => root,
         };
         Some(root.join(target.file_stem()))
-    });
+    }).collect();
+    exes.sort();
 
-    for exe in exes {
+    let cwd = os::getcwd();
+    for exe in exes.iter() {
+        let to_display = match exe.path_relative_from(&cwd) {
+            Some(path) => path,
+            None => exe.clone(),
+        };
+        try!(options.shell.status("Running", to_display.display()));
         match process(exe).args(args).exec() {
             Ok(()) => {}
             Err(e) => return Ok(Some(e))
         }
     }
 
+    let mut libs = package.get_targets().iter().filter_map(|target| {
+        if !target.get_profile().is_test() || !target.is_lib() {
+            return None
+        }
+        Some((target.get_src_path(), target.get_name()))
+    });
+
+    for (lib, name) in libs {
+        try!(options.shell.status("Doc-tests", name));
+        let mut p = process("rustdoc").arg("--test").arg(lib)
+                                      .arg("--crate-name").arg(name)
+                                      .arg("-L").arg("target/test")
+                                      .arg("-L").arg("target/test/deps")
+                                      .cwd(package.get_root());
+
+        // FIXME(rust-lang/rust#16272): this should just always be passed.
+        if args.len() > 0 {
+            p = p.arg("--test-args").arg(args.connect(" "));
+        }
+
+        for (pkg, libs) in compiled_libs.iter() {
+            for lib in libs.iter() {
+                let mut arg = pkg.get_name().as_bytes().to_vec();
+                arg.push(b'=');
+                arg.push_all(lib.as_vec());
+                p = p.arg("--extern").arg(arg.as_slice());
+            }
+        }
+
+        match p.exec() {
+            Ok(()) => {}
+            Err(e) => return Ok(Some(e)),
+        }
+    }
+
     Ok(None)
 }
index 22196e6701bc01eee2adf7222de096ec8f818ea7..b0bb5b4fd4da88bc26c76d4a74bff1f383380c91 100644 (file)
@@ -623,7 +623,7 @@ fn normalize(libs: &[TomlLibTarget],
                         |bin| format!("src/bin/{}.rs", bin.name));
         },
         ([_, ..], []) => {
-            lib_targets(&mut ret, libs, test_dep, metadata);
+            lib_targets(&mut ret, libs, Needed, metadata);
         },
         ([], [_, ..]) => {
             bin_targets(&mut ret, bins, test_dep, metadata,
index 4de930804c9a4618c3aaa5c0d1cc5bd02303f636..080e4349ed36073e0a4b932494d2edfed08bdaed 100644 (file)
@@ -499,3 +499,4 @@ pub static RUNNING:   &'static str = "     Running";
 pub static COMPILING: &'static str = "   Compiling";
 pub static FRESH:     &'static str = "       Fresh";
 pub static UPDATING:  &'static str = "    Updating";
+pub static DOCTEST:   &'static str = "   Doc-tests";
index 54d1490f16b21c6f22da5327419dabd57b0b23f5..3b4a84e8a9dd8344f0b7ff8ddceae6561f9a4ce8 100644 (file)
@@ -70,7 +70,6 @@ test!(modify_only_some_files {
 
     let lib = p.root().join("src/lib.rs");
     let bin = p.root().join("src/b.rs");
-    let test = p.root().join("tests/test.rs");
 
     File::create(&lib).write_str("invalid rust code").assert();
     lib.move_into_the_past().assert();
@@ -85,9 +84,4 @@ test!(modify_only_some_files {
 {compiling} foo v0.0.1 (file:{dir})
 ", compiling = COMPILING, dir = p.root().display())));
     assert_that(&p.bin("foo"), existing_file());
-
-    // Make sure the tests don't recompile the lib
-    File::create(&test).write_str("fn foo() {}").assert();
-    assert_that(p.process(cargo_dir().join("cargo-test")),
-                execs().with_status(0));
 })
index 56878e55e084f85f986353d06f4b940aa1fe85b9..15cb1e8e4dafbb3b322c732c4a63fab42f1ae717 100644 (file)
@@ -2,7 +2,7 @@ use std::path;
 use std::str;
 
 use support::{project, execs, basic_bin_manifest, basic_lib_manifest};
-use support::{COMPILING, cargo_dir, ResultTest, FRESH};
+use support::{COMPILING, cargo_dir, ResultTest, FRESH, RUNNING, DOCTEST};
 use support::paths::PathExt;
 use hamcrest::{assert_that, existing_file};
 use cargo::util::process;
@@ -34,11 +34,16 @@ test!(cargo_test_simple {
         execs().with_stdout("hello\n"));
 
     assert_that(p.process(cargo_dir().join("cargo-test")),
-        execs().with_stdout(format!("{} foo v0.5.0 (file:{})\n\n\
-                                    running 1 test\n\
-                                    test test_hello ... ok\n\n\
-                                    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n",
-                                    COMPILING, p.root().display())));
+        execs().with_stdout(format!("\
+{} foo v0.5.0 (file:{})
+{} target[..]test[..]foo
+
+running 1 test
+test test_hello ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n",
+        COMPILING, p.root().display(),
+        RUNNING)));
 })
 
 test!(many_similar_names {
@@ -95,19 +100,30 @@ test!(cargo_test_failing_test {
         execs().with_stdout("hello\n"));
 
     assert_that(p.process(cargo_dir().join("cargo-test")),
-        execs().with_stdout(format!("{} foo v0.5.0 (file:{})\n\n\
-                                    running 1 test\n\
-                                    test test_hello ... FAILED\n\n\
-                                    failures:\n\n\
-                                    ---- test_hello stdout ----\n<tab>\
-                                    task 'test_hello' failed at 'assertion failed: \
-                                    `(left == right) && (right == left)` (left: \
-                                    `hello`, right: `nope`)', src{sep}foo.rs:12\n<tab>\n<tab>\n\n\
-                                    failures:\n    test_hello\n\n\
-                                    test result: FAILED. 0 passed; 1 failed; \
-                                    0 ignored; 0 measured\n\n",
-                                    COMPILING, p.root().display(),
-                                    sep = path::SEP))
+        execs().with_stdout(format!("\
+{} foo v0.5.0 (file:{})
+{} target[..]test[..]foo
+
+running 1 test
+test test_hello ... FAILED
+
+failures:
+
+---- test_hello stdout ----
+<tab>task 'test_hello' failed at 'assertion failed: \
+    `(left == right) && (right == left)` (left: \
+    `hello`, right: `nope`)', src{sep}foo.rs:12
+<tab>
+<tab>
+
+failures:
+    test_hello
+
+test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
+
+",
+        COMPILING, p.root().display(), RUNNING,
+        sep = path::SEP))
               .with_stderr(format!("\
 task '<main>' failed at 'Some tests failed', [..]
 Could not execute process `{test}[..]` (status=101)
@@ -127,10 +143,18 @@ test!(test_with_lib_dep {
             name = "baz"
             path = "src/main.rs"
         "#)
-        .file("src/lib.rs", "
+        .file("src/lib.rs", r#"
+            ///
+            /// ```rust
+            /// extern crate foo;
+            /// fn main() {
+            ///     println!("{}", foo::foo());
+            /// }
+            /// ```
+            ///
             pub fn foo(){}
             #[test] fn lib_test() {}
-        ")
+        "#)
         .file("src/main.rs", "
             extern crate foo;
 
@@ -140,26 +164,32 @@ test!(test_with_lib_dep {
             fn bin_test() {}
         ");
 
-    let output = p.cargo_process("cargo-test")
-                  .exec_with_output().assert();
-    let out = str::from_utf8(output.output.as_slice()).assert();
+    assert_that(p.cargo_process("cargo-test"),
+        execs().with_stdout(format!("\
+{} foo v0.0.1 (file:{})
+{running} target[..]test[..]baz-[..]
 
-    let bin = "\
 running 1 test
 test bin_test ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
-    let lib = "\
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{running} target[..]test[..]foo
+
 running 1 test
 test lib_test ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
 
-    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
-                       compiling = COMPILING, dir = p.root().display());
+{doctest} foo
 
-    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, bin, lib).as_slice() ||
-            out == format!("{}\n\n{}\n\n\n{}\n\n", head, lib, bin).as_slice());
+running 1 test
+test foo_0 ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+",
+        COMPILING, p.root().display(), running = RUNNING, doctest = DOCTEST)))
 })
 
 test!(test_with_deep_lib_dep {
@@ -200,13 +230,21 @@ test!(test_with_deep_lib_dep {
                        .with_stdout(format!("\
 {compiling} foo v0.0.1 (file:{dir})
 {compiling} bar v0.0.1 (file:{dir})
+{running} target[..]
 
 running 1 test
 test bar_test ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measure
+
+{doctest} bar
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured\n\n\
                        ",
-                       compiling = COMPILING,
+                       compiling = COMPILING, running = RUNNING,
+                       doctest = DOCTEST,
                        dir = p.root().display()).as_slice()));
 })
 
@@ -235,26 +273,31 @@ test!(external_test_explicit {
             fn external_test() { assert_eq!(foo::get_hello(), "Hello") }
         "#);
 
-    let output = p.cargo_process("cargo-test")
-                  .exec_with_output().assert();
-    let out = str::from_utf8(output.output.as_slice()).assert();
+    assert_that(p.cargo_process("cargo-test"),
+        execs().with_stdout(format!("\
+{} foo v0.0.1 (file:{})
+{running} target[..]test[..]foo-[..]
 
-    let internal = "\
 running 1 test
 test internal_test ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
-    let external = "\
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{running} target[..]test[..]test-[..]
+
 running 1 test
 test external_test ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
 
-    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
-                       compiling = COMPILING, dir = p.root().display());
+{doctest} foo
 
-    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, internal, external).as_slice() ||
-            out == format!("{}\n\n{}\n\n\n{}\n\n", head, external, internal).as_slice());
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
+
+",
+        COMPILING, p.root().display(), running = RUNNING, doctest = DOCTEST)))
 })
 
 test!(external_test_implicit {
@@ -278,26 +321,31 @@ test!(external_test_implicit {
             fn external_test() { assert_eq!(foo::get_hello(), "Hello") }
         "#);
 
-    let output = p.cargo_process("cargo-test")
-                  .exec_with_output().assert();
-    let out = str::from_utf8(output.output.as_slice()).assert();
+    assert_that(p.cargo_process("cargo-test"),
+        execs().with_stdout(format!("\
+{} foo v0.0.1 (file:{})
+{running} target[..]test[..]external-[..]
 
-    let internal = "\
 running 1 test
-test internal_test ... ok
+test external_test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{running} target[..]test[..]foo-[..]
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
-    let external = "\
 running 1 test
-test external_test ... ok
+test internal_test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{doctest} foo
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
+running 0 tests
 
-    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
-                       compiling = COMPILING, dir = p.root().display());
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
 
-    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, internal, external).as_slice() ||
-            out == format!("{}\n\n{}\n\n\n{}\n\n", head, external, internal).as_slice());
+",
+        COMPILING, p.root().display(), running = RUNNING, doctest = DOCTEST)))
 })
 
 test!(dont_run_examples {
@@ -334,26 +382,42 @@ test!(pass_through_command_line {
                 execs().with_status(0)
                        .with_stdout(format!("\
 {compiling} foo v0.0.1 (file:{dir})
+{running} target[..]test[..]foo
 
 running 1 test
 test bar ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{doctest} foo
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured\n\n\
                        ",
-                       compiling = COMPILING,
+                       compiling = COMPILING, running = RUNNING,
+                       doctest = DOCTEST,
                        dir = p.root().display()).as_slice()));
 
     assert_that(p.cargo_process("cargo-test").arg("foo"),
                 execs().with_status(0)
                        .with_stdout(format!("\
 {compiling} foo v0.0.1 (file:{dir})
+{running} target[..]test[..]foo
 
 running 1 test
 test foo ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{doctest} foo
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured\n\n\
                        ",
-                       compiling = COMPILING,
+                       compiling = COMPILING, running = RUNNING,
+                       doctest = DOCTEST,
                        dir = p.root().display()).as_slice()));
 })
 
@@ -400,27 +464,31 @@ test!(lib_bin_same_name {
             fn bin_test() {}
         ");
 
-    let output = p.cargo_process("cargo-test")
-                  .exec_with_output().assert();
-    let out = str::from_utf8(output.output.as_slice()).assert();
+    assert_that(p.cargo_process("cargo-test"),
+        execs().with_stdout(format!("\
+{} foo v0.0.1 (file:{})
+{running} target[..]test[..]foo-[..]
 
-    let bin = "\
 running 1 test
-test bin_test ... ok
+test [..] ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{running} target[..]test[..]foo-[..]
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
-    let lib = "\
 running 1 test
-test lib_test ... ok
+test [..] ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
+{doctest} foo
 
-    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
-                       compiling = COMPILING, dir = p.root().display());
+running 0 tests
 
-    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, bin, lib).as_slice() ||
-            out == format!("{}\n\n{}\n\n\n{}\n\n", head, lib, bin).as_slice(),
-            "bad output: {}", out);
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
+
+",
+        COMPILING, p.root().display(), running = RUNNING, doctest = DOCTEST)))
 })
 
 test!(lib_with_standard_name {
@@ -449,13 +517,14 @@ test!(lib_with_standard_name {
                 execs().with_status(0)
                        .with_stdout(format!("\
 {compiling} syntax v0.0.1 (file:{dir})
+{running} target[..]test[..]test-[..]
 
 running 1 test
 test test ... ok
 
 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
                        ",
-                       compiling = COMPILING,
+                       compiling = COMPILING, running = RUNNING,
                        dir = p.root().display()).as_slice()));
 })
 
@@ -487,13 +556,14 @@ test!(lib_with_standard_name2 {
                 execs().with_status(0)
                        .with_stdout(format!("\
 {compiling} syntax v0.0.1 (file:{dir})
+{running} target[..]test[..]syntax-[..]
 
 running 1 test
 test test ... ok
 
 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
                        ",
-                       compiling = COMPILING,
+                       compiling = COMPILING, running = RUNNING,
                        dir = p.root().display()).as_slice()));
 })
 
@@ -545,26 +615,42 @@ test!(test_dylib {
                 execs().with_status(0)
                        .with_stdout(format!("\
 {compiling} foo v0.0.1 (file:{dir})
+{running} target[..]test[..]foo-[..]
 
 running 1 test
 test foo ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{doctest} foo
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured\n\n\
                        ",
-                       compiling = COMPILING,
+                       compiling = COMPILING, running = RUNNING,
+                       doctest = DOCTEST,
                        dir = p.root().display()).as_slice()));
     p.root().move_into_the_past().assert();
     assert_that(p.process(cargo_dir().join("cargo-test")),
                 execs().with_status(0)
                        .with_stdout(format!("\
 {fresh} foo v0.0.1 (file:{dir})
+{running} target[..]test[..]foo-[..]
 
 running 1 test
 test foo ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{doctest} foo
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured\n\n\
                        ",
-                       fresh = FRESH,
+                       fresh = FRESH, running = RUNNING,
+                       doctest = DOCTEST,
                        dir = p.root().display()).as_slice()));
 })
 
@@ -586,25 +672,41 @@ test!(test_twice_with_build_cmd {
                 execs().with_status(0)
                        .with_stdout(format!("\
 {compiling} foo v0.0.1 (file:{dir})
+{running} target[..]test[..]foo-[..]
 
 running 1 test
 test foo ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{doctest} foo
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured\n\n\
                        ",
-                       compiling = COMPILING,
+                       compiling = COMPILING, running = RUNNING,
+                       doctest = DOCTEST,
                        dir = p.root().display()).as_slice()));
 
     assert_that(p.process(cargo_dir().join("cargo-test")),
                 execs().with_status(0)
                        .with_stdout(format!("\
 {fresh} foo v0.0.1 (file:{dir})
+{running} target[..]test[..]foo-[..]
 
 running 1 test
 test foo ... ok
 
-test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+{doctest} foo
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured\n\n\
                        ",
-                       fresh = FRESH,
+                       fresh = FRESH, running = RUNNING,
+                       doctest = DOCTEST,
                        dir = p.root().display()).as_slice()));
 })